﻿using System.Threading.Tasks;
using K9Nano.Authorization;
using K9Nano.Domain.Caching;
using K9Nano.Domain.Entities;
using K9Nano.Exceptions;
using K9Nano.GlobalOptions;
using K9Nano.Mapping;
using Microsoft.AspNetCore.Http.HttpResults;
using Microsoft.EntityFrameworkCore;

namespace K9Nano.Application.Authentication;

public class RoleAppServiceBase<TUser, TRole, TUserRole, TRolePermission, TDbContext>(TDbContext db, IPermissionCatalog permissionCatalog, IUserCache userCache)
    : CurdAppServiceBase<TRole, TDbContext, RoleCreateDto, RoleEditDto, RoleDto, RoleQueryDto<TRole>>(db)
    , IRoleAppServiceBase<TRole>
    where TRole : RoleBase
    where TUser : UserBase
    where TRolePermission : RolePermissionBase<TRole>, new()
    where TUserRole : UserRoleBase<TUser, TRole>, new()
    where TDbContext : DbContext
{
    public virtual async Task AddPermissionsAsync(RolePermissionEditDto dto, CancellationToken cancellation)
    {
        if (!await db.Set<TRole>().AnyAsync(a => a.Id.Equals(dto.RoleId), cancellation))
        {
            throw FriendlyException.BadRequest("角色不存在: " + dto.RoleId);
        }

        if (dto.Permissions is null || dto.Permissions.Length == 0)
        {
            throw new FriendlyException("权限列表不能为空");
        }

        var role = await db.Set<TRole>().FirstOrDefaultAsync(a => a.Id == dto.RoleId, cancellation)
            ?? throw new FriendlyException($"Id为{dto.RoleId}的角色不存在");
        if (RoleNames.Admin.Equals(role.Name, System.StringComparison.OrdinalIgnoreCase))
        {
            throw new FriendlyException("不能修改超级管理员角色的权限");
        }

        var exists = await db.Set<TRolePermission>()
            .Where(a => a.RoleId == dto.RoleId && dto.Permissions.Contains(a.Permission))
            .Select(a => a.Permission)
            .ToListAsync(cancellation);

        var rp = dto.Permissions
            .Where(a => !exists.Contains(a))
            .Select(a => new TRolePermission
            {
                RoleId = dto.RoleId,
                Permission = a
            });
        db.AddRange(rp);
        await db.SaveChangesAsync(cancellation);

        await ClearUserCacheAsync(dto.RoleId);
    }

    public virtual async Task RemovePermissionsAsync(RolePermissionEditDto dto, CancellationToken cancellation)
    {
        if (!await db.Set<TRole>().AnyAsync(a => a.Id.Equals(dto.RoleId), cancellation))
        {
            throw FriendlyException.BadRequest("角色不存在: " + dto.RoleId);
        }

        if (dto.Permissions is null || dto.Permissions.Length == 0)
        {
            throw new FriendlyException("权限列表不能为空");
        }

        var role = await db.Set<TRole>().FirstOrDefaultAsync(a => a.Id == dto.RoleId, cancellation)
            ?? throw new FriendlyException($"Id为{dto.RoleId}的角色不存在");

        if (RoleNames.Admin.Equals(role.Name, System.StringComparison.OrdinalIgnoreCase))
        {
            throw new FriendlyException("不能修改超级管理员角色的权限");
        }

        await db.Set<TRolePermission>()
             .Where(a => a.RoleId.Equals(dto.RoleId) && dto.Permissions.Contains(a.Permission))
             .ExecuteDeleteAsync(cancellation);

        await ClearUserCacheAsync(dto.RoleId);
    }

    public override async Task<RoleDto> CreateAsync(RoleCreateDto createDto, CancellationToken cancellation)
    {
        if (await db.Set<TRole>().AnyAsync(a => a.Code == createDto.Code, cancellation))
        {
            throw FriendlyException.BadRequest("角色代码已存在: " + createDto.Code);
        }
        var result = await base.CreateAsync(createDto, cancellation);
        if (RoleNames.Admin.Equals(result.Code, System.StringComparison.Ordinal))
        {
            // 超级管理员拥有所有权限，不需要处理权限数据
            return result;
        }

        if (createDto.Permissions?.Length > 0)
        {
            var entities = createDto.Permissions.Select(a => new TRolePermission { RoleId = result.Id, Permission = a }).ToList();
            db.AddRange(entities);
            await db.SaveChangesAsync(CancellationToken.None);
        }
        return result;
    }

    public override async Task<RoleDto> UpdateAsync(RoleEditDto updateDto, CancellationToken cancellation)
    {
        if (await db.Set<TRole>().AnyAsync(a => a.Id != updateDto.Id && a.Code == updateDto.Code, cancellation))
        {
            throw FriendlyException.BadRequest("角色代码已存在: " + updateDto.Code);
        }
        var result = await base.UpdateAsync(updateDto, cancellation);

        if (RoleNames.Admin.Equals(result.Code, System.StringComparison.Ordinal))
        {
            // 超级管理员拥有所有权限，不需要处理权限数据
            return result;
        }

        await db.Set<TRolePermission>().Where(a => a.RoleId == updateDto.Id).ExecuteDeleteAsync(CancellationToken.None);

        if (updateDto.Permissions?.Length > 0)
        {
            var entities = updateDto.Permissions.Select(a => new TRolePermission { RoleId = result.Id, Permission = a }).ToList();
            db.AddRange(entities);
            await db.SaveChangesAsync(CancellationToken.None);
        }

        await ClearUserCacheAsync(updateDto.Id);

        return result;
    }

    /// <inheritdoc/>
    public virtual async Task<IEnumerable<Permission>> GetPermissionsByRoleIdAsync(long roleId, CancellationToken cancellation)
    {
        var permissions = await db.Set<TRolePermission>().AsNoTracking().Where(a => a.RoleId == roleId)
              .Select(a => a.Permission)
              .ToListAsync(cancellation);
        if (permissions.Count <= 0)
        {
            return [];
        }

        var result = permissionCatalog.Permissions.Where(a => permissions.Contains(a.Name, StringComparer.Ordinal)).ToList();
        return result;
    }

    /// <inheritdoc/>
    public virtual async Task<IEnumerable<UserDto>> GetUsersByRoleIdAsync(long roleId, CancellationToken cancellation)
    {
        var users = await db.Set<TUserRole>().Include(a => a.User).AsNoTracking().Where(a => a.RoleId == roleId)
              .Select(a => a.User)
              .ToListAsync(cancellation);

        var result = MappingHelper.Mapper.Map<List<UserDto>>(users);
        return result;
    }

    /// <summary>
    /// 角色权限变更后，清除拥有此角色的用户缓存
    /// </summary>
    /// <param name="roleId"></param>
    protected virtual async Task ClearUserCacheAsync(long roleId)
    {
        var users = await db.Set<TUserRole>().AsNoTracking().Where(a => a.RoleId == roleId)
             .Select(a => a.UserId)
             .ToListAsync(CancellationToken.None);
        foreach (var user in users)
        {
            userCache.Remove(user);
        }
    }
}